package com.morpheus.redis.jedis.config;

import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import com.morpheus.redis.jedis.annotation.ConditionalOnJedisSentinelConfig;

import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;

@Configuration
@EnableConfigurationProperties({ RedisProperties.class, JedisProperties.class })
@ConditionalOnJedisSentinelConfig
public class JedisSentinelAutoConfiguration {
	private static final Logger LOGGER = LoggerFactory.getLogger(JedisSentinelAutoConfiguration.class);
	private static final String DEFAULT_JMX_NAME_BASE = "jedis";
	private static final String DEFAULT_JMX_NAME_PREFIX = "jmx";

	@Bean
	public JedisPoolConfig jedisPoolConfig(RedisProperties redisProperties, JedisProperties jedisProperties) {
		JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
		jedisPoolConfig.setJmxEnabled(jedisProperties.getJmx().isEnabled());
		jedisPoolConfig.setJmxNameBase(
				Optional.ofNullable(jedisProperties.getJmx().getName().getBase()).orElse(DEFAULT_JMX_NAME_BASE));
		jedisPoolConfig.setJmxNamePrefix(
				Optional.ofNullable(jedisProperties.getJmx().getName().getPrefix()).orElse(DEFAULT_JMX_NAME_PREFIX));
		jedisPoolConfig.setMaxIdle(redisProperties.getJedis().getPool().getMaxIdle());
		jedisPoolConfig.setMinIdle(redisProperties.getJedis().getPool().getMinIdle());
		jedisPoolConfig.setMaxTotal(redisProperties.getJedis().getPool().getMaxActive());
		jedisPoolConfig.setMaxWaitMillis(redisProperties.getJedis().getPool().getMaxWait().toMillis());
		jedisPoolConfig.setTimeBetweenEvictionRunsMillis(
				redisProperties.getJedis().getPool().getTimeBetweenEvictionRuns().toMillis());
		jedisPoolConfig.setTestOnBorrow(jedisProperties.getTest().isOnBorrow());
		jedisPoolConfig.setTestOnCreate(jedisProperties.getTest().isOnCreate());
		jedisPoolConfig.setTestOnReturn(jedisProperties.getTest().isOnReturn());
		jedisPoolConfig.setTestWhileIdle(jedisProperties.getTest().isWhileIdle());
		LOGGER.debug("JedisSentinelAutoConfiguration.jedisPoolConfig() jedisPoolConfig={}", jedisPoolConfig);
		return jedisPoolConfig;
	}

	@Bean
	public RedisSentinelConfiguration sentinelConfiguration(RedisProperties redisProperties) {
		List<String> sentinelNodes = redisProperties.getSentinel().getNodes();
		Set<String> sentinels = new HashSet<>();
		sentinels.addAll(sentinelNodes);
		RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration(
				redisProperties.getSentinel().getMaster(), sentinels);
		LOGGER.debug("JedisSentinelAutoConfiguration.sentinelConfiguration() redisSentinelConfiguration={}",
				redisSentinelConfiguration);
		return redisSentinelConfiguration;
	}

	@Bean
	public JedisSentinelPool jedisSentinelPool(JedisPoolConfig poolConfig, RedisProperties redisProperties,
			JedisProperties jedisProperties) {
		List<String> sentinelList = redisProperties.getSentinel().getNodes();
		Set<String> sentinels = new HashSet<String>();
		sentinels.addAll(sentinelList);
		JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(redisProperties.getSentinel().getMaster(),
				sentinels, poolConfig, Long.valueOf(redisProperties.getTimeout().toMillis()).intValue(),
				jedisProperties.getSoTimeout(), redisProperties.getPassword(), redisProperties.getDatabase());
		LOGGER.debug("JedisSentinelAutoConfiguration.jedisSentinelPool() jedisSentinelPool={}", jedisSentinelPool);
		return jedisSentinelPool;
	}

	@Bean(destroyMethod = "destroy")
	public JedisConnectionFactory redisConnectionFactory(RedisSentinelConfiguration sentinelConfig,
			JedisPoolConfig poolConfig) {
		JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(sentinelConfig, poolConfig);
		LOGGER.debug("JedisSentinelAutoConfiguration.redisConnectionFactory() redisConnectionFactory={}",
				redisConnectionFactory);
		return redisConnectionFactory;
	}

	@Bean
	public StringRedisTemplate stringRedisTemplate(JedisConnectionFactory connectionFactory) {
		StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(connectionFactory);
		return stringRedisTemplate;
	}

	@Bean
	public RedisTemplate<Object, Object> redisTemplate(JedisConnectionFactory connectionFactory) {
		RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
		redisTemplate.setConnectionFactory(connectionFactory);
		return redisTemplate;
	}
}
